/*
 * Copyright Notice ====================================================
 * This file contains proprietary information of Hewlett-Packard Co.
 * Copying or reproduction without prior written approval is prohibited.
 * Copyright (c) 2013 All rights reserved. =============================
 */

package com.hp.ecs.xqa.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.hp.ecs.xqa.vo.SonarProject;
import com.hp.ecs.xqa.vo.SonarServer;
import com.hp.ecs.xqa.vo.Violation;

public class SonarClient {

	private boolean logined;

	private SonarServer server;

	public void setSonarServer(SonarServer server) {
		this.server = server;
	}

	public boolean login() {
		if (logined) {
			return logined;
		}
		Map<String, String> paras = new HashMap();
		paras.put("login", server.getUser());
		paras.put("password", server.getPassword());
		String content = doPost("sessions/login", paras);
		logined = content.contains("redirected");
		return logined;
	}

	private String cookieVal;

	private String doPost(String path, Map<String, String> paras) {
		return this.access(path, "POST", paras);
	}

	private String doGet(String path, Map<String, String> paras) {
		return this.access(path, "GET", paras);
	}

	private String access(String path, String method, Map<String, String> paras) {
		HttpsURLConnection con = null;
		try {
			SSLSocketFactory ssf = getSSL();

			URL myURL = new URL(buildUrl(path, paras)); // 创建URL对象
			// 创建HttpsURLConnection对象，并设置其SSLSocketFactory对象
			con = (HttpsURLConnection)myURL.openConnection();
			if (cookieVal != null){
				con.addRequestProperty("Cookie", cookieVal);
			}
			con.addRequestProperty("Cache-Control","no-cache");
			con.setDoInput(true);
			con.setDoOutput(true);
			con.setSSLSocketFactory(ssf);
			con.setRequestMethod(method);
			// 创建Get方法实例
			readCookie(con);
			int count = this.getLength(con);
			return new String(IoUtil.read(con.getInputStream(), count), "UTF-8");
		} catch (Exception e) {
			throw new IllegalStateException(e);
		} finally {
			if (con != null) {
				con.disconnect();
			}
		}
	}

	public String buildUrl(String path, Map<String, String> paras) {
		StringBuilder sb = new StringBuilder();
		sb.append(server.getRoot()).append(path);
		if (path.indexOf('?') < 0) {
			sb.append("?z");
		}
		sb.append(path.indexOf('?') < 0 ? '?' : '&').append('z');
		try {
			for (Map.Entry<String, String> me : paras.entrySet()) {
				sb.append('&').append(me.getKey()).append('=');
				sb.append(java.net.URLEncoder.encode(me.getValue(), "UTF-8"));
			}
		} catch (UnsupportedEncodingException e) {
			throw new IllegalArgumentException(e);
		}
		return sb.toString();
	}

	private int getLength(HttpsURLConnection con) {
		final List<String> list = con.getHeaderFields().get("Content-Length");
		return Integer.valueOf(list.get(0));
	}

	private void readCookie(HttpsURLConnection con) {
		for (Map.Entry<String, List<String>> me : con.getHeaderFields().entrySet()) {
			System.out.println(me.getKey() + ":" + me.getValue());
		}
		final List<String> list = con.getHeaderFields().get("Set-Cookie");
		if (list == null || list.isEmpty()) {
			return;
		}
		cookieVal = list.toString().replace(']', ' ').replace('[', ' ');
	}

	public String read(InputStreamReader is) {
		BufferedReader reader = new BufferedReader(is);
		StringBuilder sb = new StringBuilder();

		String line = null;
		try {
			while ((line = reader.readLine()) != null) {
				sb.append(line + "\n");
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return sb.toString();
	}

	private SSLSocketFactory getSSL() {
		try {
			// 创建SSLContext对象，并使用我们指定的信任管理器初始化
			TrustManager[] tm = {new X509TrustManager() {

				public void checkClientTrusted(X509Certificate[] arg0, String arg1) {
					// Empty
				}

				public void checkServerTrusted(X509Certificate[] arg0, String arg1) {
					// Empty
				}

				public X509Certificate[] getAcceptedIssuers() {
					return null;
				}
			}};
			SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
			sslContext.init(null, tm, new java.security.SecureRandom());

			// 从上述SSLContext对象中得到SSLSocketFactory对象
			return sslContext.getSocketFactory();
		} catch (Exception e) {
			throw new IllegalStateException("Failed to create SSL", e);
		}
	}

	public List<SonarProject> getProjects() {
		String html = doGet("", Collections.EMPTY_MAP);
		return NekoUtil.parase(html, new NekoUtil.Extractor<SonarProject>() {
			public SonarProject extract(Node node) {
				if (!node.getNodeName().equalsIgnoreCase("tr")) {
					return null;
				}
				final Node clazzNode = node.getAttributes().getNamedItem("class");
				if (clazzNode == null) {
					return null;
				}
				final String clazz = clazzNode.getNodeValue();
				if (!clazz.equals("even") && !clazz.equals("odd")) {
					return null;
				}

				return toProject(node);
			}

		}, false);
	}

	private SonarProject toProject(Node node) {
		final NodeList tds = node.getChildNodes();
		if (tds.getLength() < 4) {
			return null;
		}
		final NodeList projectTd = tds.item(2).getChildNodes();
		if (projectTd.getLength() != 2) {
			return null;
		}
		// <td class=" nowrap">
		// <img alt="" height="16" src="/sonar/images/q/TRK.png?1365363348" title="" width="16" />
		// <a href="/sonar/dashboard/index/195910" title="com.hp.it.113962.legacyecs:ecs:Beljawsk_1816">
		// HP Enterprise Cloud System Beljawsk_1816</a></td>

		Node ahref = projectTd.item(1);
		SonarProject project = new SonarProject();
		project.setName(ahref.getFirstChild().getNodeValue());
		project.setIdSn(getProjectId(ahref));
		project.setDate(tds.item(3).getNodeValue());
		return project;
	}

	private String getProjectId(Node ahref) {
		final String href = ahref.getAttributes().getNamedItem("href").getNodeValue();
		return href.substring(href.lastIndexOf('/') + 1);
	}

	public List<Violation> getViolationsIn7days(String projectId, final String level) {
		// drilldown/violations/164202?period=3&severity=MINOR
		String html = doGet("drilldown/violations/" + projectId + "?period=3&severity=" + level, Collections.EMPTY_MAP);
		return NekoUtil.parase(html, new NekoUtil.Extractor<Violation>() {
			public Violation extract(Node node) {
				final NodeList tds = node.getChildNodes();
				if (tds.getLength() != 2 || !node.getNodeName().equalsIgnoreCase("tr")) {
					return null;
				}
				final NodeList countTdChildren = tds.item(1).getChildNodes();
				if (countTdChildren.getLength() != 1) {
					return null;
				}
				final Node span = countTdChildren.item(0).getChildNodes().item(0);
				if (span == null || span.getAttributes() == null
					|| !"varw".equals(span.getAttributes().getNamedItem("class"))) {
					return null;
				}
				final String textContent = span.getTextContent();
				if (!textContent.matches("\\+{0,1}\\d+")) {
					return null;
				}
				Violation v = new Violation();
				v.setCount(Integer.parseInt(textContent));
				v.setLevel(level);
				if (tds.item(0).getChildNodes().getLength() < 3) {
					return null;
				}
				final Node href = tds.item(0).getChildNodes().item(0).getAttributes().getNamedItem("href");
				if (href == null) {
					return null;
				}
				final String hrefUrl = href.getNodeValue();
				if (!hrefUrl.endsWith(v.getParas())) {
					return null;
				}
				v.setFile(hrefUrl.substring(hrefUrl.indexOf(Violation.PRE_FIX) + Violation.PRE_FIX.length(),
					hrefUrl.indexOf('?')));
				final String conent = doGet(v.getContentUrl(), Collections.EMPTY_MAP);
				v.setContent(conent);
				return v;
			}

		}, false);
	}

	public void logout() {
		doGet("sessions/logout", Collections.EMPTY_MAP);
	}
}
